package cn.blnp.net.flowable.boot.custom.calendar;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.*;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;

/**
 * <h3></h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/6/13 16:06
 */
@Slf4j
@SpringBootTest
public class CusBusinessCalendarTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private IdentityService identityService;
    @Resource
    private TaskService taskService;
    @Resource
    private ManagementService managementService;

    /**
     *  <h1>自定义业务日历案例</h1>
     *  <p>
     *      <h2>Flowable 业务日历实现类</h2>
     *      <ol>
     *          <li>
     *              <b>处理timeDate配置属性的默认业务日历实现类：</b>{@link org.flowable.common.engine.impl.calendar.DueDateBusinessCalendar}
     *          </li>
     *          <li>
     *              <b>处理timeDuration配置属性的默认业务日历实现类：</b>{@link org.flowable.common.engine.impl.calendar.DurationBusinessCalendar}
     *          </li>
     *          <li>
     *              <b>处理timeCycle配置属性的默认业务日历实现类：</b>{@link org.flowable.common.engine.impl.calendar.CycleBusinessCalendar}
     *          </li>
     *          <li>
     *              <b>以上实现类的公共父类：</b>{@link org.flowable.common.engine.impl.calendar.BusinessCalendarImpl}
     *          </li>
     *      </ol>
     *  </p>
     *  <h2>流程案例</h2>
     *  <p>
     *   &emsp;&emsp;该流程为客户投诉处理流程，客服工作时间为每周一至周五的早上9点到下午6点，因此可以使用本次创建的自定义业务日历。客户提交投诉信息后，先由一线客服人员处理。
     *   如果在工作时长10小时内一线客服处理完成，流程将流转到“结案”节点；如果工作时长超过10小时一线客服仍然未能处理完成，则标记为过期未处理，如果15小时仍未处理则自动流转给二
     *   线客服人员继续处理。
     *      <ul>
     *          <li><b>流程定义文件：</b>{@link flow/timerBoundaryEventProcess.bpmn20.xml}</li>
     *          <li>
     *              <b>流程预览图：</b></br>
     *              <img src="https://cdn4.winhlb.com/2025/06/13/684be38219bf8.png" >
     *          </li>
     *      </ul>
     *  </p>
     *
     **/
    @Test
    public void test2() {
        /**
         *  流程说明：
         *      用户任务userTask2通过flowable:businessCalendarName属性指定了使用业务日历custom，通过flowable:dueDate属性设置过期时间为PT10H。定时器事件，通过其flowable:businessCalendarName
         *  属性指定了使用业务日历custom，通过timeDuration子元素指定触发时间为PT15H。
         **/
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("flow/timerBoundaryEventProcess.bpmn20.xml")
                .name("自定义流程元素")
                .deploy();

        //流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义ID：{}", processDefinition.getId());
        //设置流程发起人
        identityService.setAuthenticatedUserId("张三");

        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(processDefinition.getId());
        log.info("流程实例ID：{}", procInst.getId());
        //查询并办理第一个用户任务
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());
        Task task1 = taskQuery.singleResult();
        taskService.complete(task1.getId());
        //查询第二个用户任务
        Task task2 = taskQuery.singleResult();
        log.info("任务创建时间：{}，过期时间：{}", DateUtil.formatDateTime(task2.getCreateTime()), DateUtil.formatDateTime(task2.getDueDate()));

        //查询定时器任务
        managementService.executeCommand(new Command<Void>() {
            @Override
            public Void execute(CommandContext commandContext) {
                List<TimerJobEntity> list = CommandContextUtil.getTimerJobService()
                        .findTimerJobsByProcessInstanceId(procInst.getId());
                list.stream().forEach(timerJob -> log.info("TimerJob 创建时间：{}，触发时间：{}",
                        DateUtil.formatDateTime(timerJob.getCreateTime()), DateUtil.formatDateTime(timerJob.getDuedate())));
                return null;
            }
        });

        /**
         *  流程演示用意说明：
         *  1、案例的整体流程步骤大致是，首先初始化工作流引擎并部署流程，然后发起流程，查询并办理第一个用户任务，接着查询第二个用户任务并打印其创建时间和过期时间，最后查询
         *  定时器任务并打印创建时间和触发时间。
         *
         * 执行结果：
         * 2025-06-13 17:03:41.024  INFO 46456 --- [           main] c.b.n.f.b.c.c.CusBusinessCalendarTest    : 流程定义ID：timerBoundaryEventProcess:3:202504
         * 2025-06-13 17:03:41.086  INFO 46456 --- [           main] c.b.n.f.b.c.c.CusBusinessCalendarTest    : 流程实例ID：202505
         * 2025-06-13 17:03:46.775  INFO 46456 --- [           main] c.b.n.f.b.c.c.CusBusinessCalendarTest    : 任务创建时间：2025-06-13 17:03:41，过期时间：2025-06-17 09:03:46
         * 2025-06-13 17:03:46.784  INFO 46456 --- [           main] c.b.n.f.b.c.c.CusBusinessCalendarTest    : TimerJob 创建时间：2025-06-13 17:03:46，触发时间：2025-06-17 14:03:46
         *
         *  执行结果说明：
         *   从执行结果可以看出：用户任务创建的时间为 2025-06-13 17:03:41，过自定义业务日历计算得到的10小时工作时间后的过期时间为 2025-06-17 09:03:46；边界定时器的定时器任务创建时间为
         *  2025-06-13 17:03:46，经过自定义业务日历计算得到的15个小时工作时间后的触发时间为 2025-06-17 14:03:46，均满足业务需求。
         **/
    }
}
